extends WeaponComponent


class_name GunWeapon


## 枪口位置
@export var muzzle_marker: Node2D = null
## 子弹工厂
@export var factory: INodeFactory = null
## 子弹属性
@export var source: ProjectileResource = null


# 当前已经发射出去的子弹
var bullets: Array[Projectile] = []


func __check_exports() -> Dictionary:
    return IComponent.merge({
        "muzzle_marker": IComponent.find_child.bind(self, func(x): return x is Node2D, muzzle_marker),
        "factory": IComponent.find_child.bind(self, func(x): return x is INodeFactory, factory),
        "source": func(): return source != null,
    }, super.__check_exports())


func wipe() -> void:
    for b in bullets:
        b.teardown()
    bullets.clear()
    factory.wipe()

func cancel() -> void:
    wipe()
    super.cancel()

func spawn(arg: ProjectileResource, bullet_factory: INodeFactory = null) -> Projectile:
    var bullet: Projectile = bullet_factory.create() if bullet_factory else factory.create()
    bullet.weapon = self
    bullet.setup(arg)
    bullet.event_hit_something.connect(__on_bullet_hit_something)
    bullet.event_hit_nothing.connect(__on_bullet_hit_nothing)
    bullet.event_hit_out_of_screen.connect(__on_bullet_hit_out_of_screen)
    bullets.append(bullet)
    # 是自身发射的子弹
    if bullet_factory == null:
        bullet.event_destroy.connect(__destroy_bullet)
    return bullet

func attack() -> void:
    source.start_direction = Vector2.from_angle(muzzle_marker.global_rotation)
    source.start_position = muzzle_marker.global_position
    var bullet: Projectile = spawn(source)
    if CharacterUnit.hit_execute(__mount_character, source, bullet):
        __erase_bullet(bullet)

func __destroy_bullet(bullet: Projectile) -> void:
    # 回收自己发射的子弹
    bullet.event_destroy.disconnect(__destroy_bullet)
    factory.destroy(bullet)

func __erase_bullet(bullet: Projectile) -> void:
    bullet.event_hit_nothing.disconnect(__on_bullet_hit_nothing)
    bullet.event_hit_something.disconnect(__on_bullet_hit_something)
    bullet.event_hit_out_of_screen.disconnect(__on_bullet_hit_out_of_screen)
    bullets.erase(bullet)
    bullet.teardown()

func __on_bullet_hit_something(node: Projectile, body: Node2D, body_rid: RID, hit_direction: Vector2, hit_normal: Vector2) -> void:
    if CharacterUnit.hit_something(__mount_character, node, body, body_rid, hit_direction, hit_normal):
        return
    __erase_bullet(node)

func __on_bullet_hit_nothing(node: Projectile) -> void:
    if CharacterUnit.hit_nothing(__mount_character, node):
        return
    __erase_bullet(node)

func __on_bullet_hit_out_of_screen(node: Projectile, hit_direction: Vector2) -> void:
    if CharacterUnit.hit_out_of_screen(__mount_character, node, hit_direction):
        return
    __erase_bullet(node)
